<?php


namespace EasySwoole\EasySwoole;


use App\Process\KafkaConsumer;
use App\Toolys\IpList;
use EasySwoole\AtomicLimit\AtomicLimit;
use EasySwoole\Component\Process\AbstractProcess;
use EasySwoole\EasySwoole\AbstractInterface\Event;
use EasySwoole\EasySwoole\Crontab\Exception\Exception;
use EasySwoole\EasySwoole\Swoole\EventRegister;
use EasySwoole\FileWatcher\FileWatcher;
use EasySwoole\FileWatcher\WatchRule;
use EasySwoole\ORM\Db\Connection;
use EasySwoole\ORM\DbManager;
use EasySwoole\Component\Di;
use EasySwoole\Http\Message\Status;
use App\Toolys\Session;
use EasySwoole\Session\FileSession;
use EasySwoole\Utility\Random;

class EasySwooleEvent implements Event
{
    /**
     * @throws \EasySwoole\RedisPool\RedisPoolException
     * @throws \EasySwoole\RedisPool\Exception\Exception
     * @throws \EasySwoole\Pool\Exception\Exception
     */
    public static function initialize()
    {
        date_default_timezone_set('Asia/Shanghai');

        $instance = \EasySwoole\EasySwoole\Config::getInstance();//配置获取

        ###### 注册 redis 连接池 ######
        $redisConfigClass = new \EasySwoole\Redis\Config\RedisConfig();
        $redisConfigClass->setHost($instance->getConf('REDIS.host'));
        $redisConfigClass->setPort($instance->getConf('REDIS.port'));
        $redisConfigClass->setAuth($instance->getConf('REDIS.auth'));
        $redisPoolConfig = \EasySwoole\RedisPool\RedisPool::getInstance()->register($redisConfigClass);
        $redisPoolConfig->setMinObjectNum($instance->getConf('REDIS.minObjectNum'));//配置连接池最小连接数
        $redisPoolConfig->setMaxObjectNum($instance->getConf('REDIS.maxObjectNum'));//配置连接池最大连接数

        ###### 注册 mysql orm 连接池 ######
        $config = new \EasySwoole\ORM\Db\Config(Config::getInstance()->getConf('MYSQL'));
        DbManager::getInstance()->addConnection(new Connection($config));

        ###### 自定义设置 shutdown: easySwoole stop 事件 ######
        \EasySwoole\Component\Di::getInstance()->set(\EasySwoole\EasySwoole\SysConst::SHUTDOWN_FUNCTION, function () {
            // 开发者对 shutdown 进行处理
        });

        ###### 自定义设置 HttpException 全局处理器 ######
        /*\EasySwoole\Component\Di::getInstance()->set(\EasySwoole\EasySwoole\SysConst::HTTP_EXCEPTION_HANDLER, function ($throwable, Request $request, Response $response) {
            $response->withStatus(\EasySwoole\Http\Message\Status::CODE_INTERNAL_SERVER_ERROR);
            $response->write(nl2br($throwable->getMessage() . "\n" . $throwable->getTraceAsString()));
            Trigger::getInstance()->throwable($throwable);
        });*/

        ###### Session 文件位置指定 ######
        Session::getInstance(new FileSession(EASYSWOOLE_TEMP_DIR.'/Session'));

        ###### 全局onRequest事件 ######
        \EasySwoole\Component\Di::getInstance()->set(\EasySwoole\EasySwoole\SysConst::HTTP_GLOBAL_ON_REQUEST,
            function (\EasySwoole\Http\Request $request, \EasySwoole\Http\Response $response): bool {
            ###### 处理请求的跨域问题 ######
            $origin = $request->getHeader('origin')[0] ?? '*';
            $response->withHeader('Access-Control-Allow-Origin', $origin);
            $response->withHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, DELETE, OPTIONS');
            $response->withHeader('Access-Control-Allow-Credentials', 'true');
            $response->withHeader('Access-Control-Allow-Headers', 'Content-Type, Token, Authorization, X-Requested-With, Language');
            if ($request->getMethod() === 'OPTIONS') {
                $response->withStatus(Status::CODE_OK);
                return false;
            }

            // 获取客户端 Cookie 中 easy_session 参数
            $sessionId = $request->getCookieParams('session');
            if (!$sessionId) {
                $sessionId = Random::character(51); // 生成 sessionId
                // 设置向客户端响应 Cookie 中 easy_session 参数
                $response->setCookie('session', $sessionId);
            }
            // 存储 sessionId 方便调用，也可以通过其它方式存储
            $request->withAttribute('session', $sessionId);
            Session::getInstance()->create($sessionId); // 创建并返回该 sessionId 的 context

            return true;
        });

        ###### 全局afterRequest 事件 ######
        \EasySwoole\Component\Di::getInstance()->set(\EasySwoole\EasySwoole\SysConst::HTTP_GLOBAL_AFTER_REQUEST,
            function (\EasySwoole\Http\Request $request, \EasySwoole\Http\Response $response): void {
                //session 数据落地【必不可少这一步】
                //try {
                    $sessionId = $request->getAttribute('session');
                    if (!empty($sessionId)){
                        Session::getInstance()->close($sessionId);
                    }
                //}catch (Exception $e){}

            // 示例：获取此次请求响应的内容 链路追踪
            //TrackerManager::getInstance()->getTracker()->endPoint('request');
            //$responseMsg = $response->getBody()->__toString();
            //Logger::getInstance()->console('响应内容:' . $responseMsg);
            // var_dump($response->getStatusCode());// 响应状态码：
            //TrackerManager::getInstance()->closeTracker();// tracker 结束，结束之后，能看到中途设置的参数，调用栈的运行情况
        });

    }


    public static function mainServerCreate(EventRegister $register)
    {
        ###### MYSQL链接预热 ######
        $register->add($register::onWorkerStart,function (){
            DbManager::getInstance()->getConnection()->__getClientPool()->keepMin();
        });

        ###### 开发模式下执行的逻辑 ######
        if (\EasySwoole\EasySwoole\Core::getInstance()->runMode() == 'dev') {
            ###### 开启热重载 ######
            $watcher = new FileWatcher();
            $rule = new WatchRule(EASYSWOOLE_ROOT . "/App"); // 设置监控规则和监控目录
            $watcher->addRule($rule);
            $watcher->setOnChange(function () {
                Logger::getInstance()->info('file change ,reload!!!');
                ServerManager::getInstance()->getSwooleServer()->reload();
            });
            $watcher->attachServer(ServerManager::getInstance()->getSwooleServer());
            //print_r("file change ,hotReload starting...".PHP_EOL);
        }

        ###### fastCache ######
        $config = new \EasySwoole\FastCache\Config();
        $config->setTempDir(EASYSWOOLE_TEMP_DIR);
        \EasySwoole\FastCache\Cache::getInstance($config)->attachToServer(ServerManager::getInstance()->getSwooleServer());

        ###### fastCacheQueue 性能极高但不能分布式 ######
        \EasySwoole\Component\Process\Manager::getInstance()->addProcess(new \App\Process\FastCachQueue(new \EasySwoole\Component\Process\Config([
            'processName' => 'fastCacheQueue', // 设置 自定义进程名称
            'processGroup' => 'Custom', // 设置 自定义进程组名称
            'arg' => [

            ], // 【可选参数】设置 注册进程时要传递给自定义进程的参数，可在自定义进程中通过 $this->getArg() 进行获取
            'enableCoroutine' => true, // 设置 自定义进程自动开启协程
        ])));

        ###### KAFKA 消费者 ######
        /*\EasySwoole\Component\Process\Manager::getInstance()->addProcess(new KafkaConsumer(new \EasySwoole\Component\Process\Config([
            'processName' => 'kafkaQueue', // 设置 自定义进程名称
            'processGroup' => 'Custom', // 设置 自定义进程组名称
            'arg' => [

            ], // 【可选参数】设置 注册进程时要传递给自定义进程的参数，可在自定义进程中通过 $this->getArg() 进行获取
            'enableCoroutine' => true, // 设置 自定义进程自动开启协程
        ])));*/

        ###### 配置限流器 ######
        $limit = new AtomicLimit();
        $limit->setLimitQps(450);/**（全局的）限制设置 */
        $limit->attachServer(ServerManager::getInstance()->getSwooleServer());
        \EasySwoole\Component\Di::getInstance()->set('auto_limiter', $limit);

        ###### IP 限流 ######
        IpList::getInstance();
        $class = new class('IpAccessCount') extends AbstractProcess
        {
            protected function run($arg)
            {
                $this->addTick(10 * 1000, function () {
                    /**
                     * 正常用户不会有一秒超过 6 次的api请求
                     * 做列表记录并清空
                     */
                    $list = IpList::getInstance()->accessList(50);
                    // var_dump($list);
                    IpList::getInstance()->clear();
                });
            }
        };
        $processConfig = new \EasySwoole\Component\Process\Config();
        $processConfig->setProcessName('IP_LIST');// 设置进程名称
        $processConfig->setProcessGroup('IP_LIST');// 设置进程组名称
        $processConfig->setArg([]);// 传参
        $processConfig->setRedirectStdinStdout(false);// 是否重定向标准io
        $processConfig->setPipeType(\EasySwoole\Component\Process\Config::PIPE_TYPE_SOCK_DGRAM);// 设置管道类型
        $processConfig->setEnableCoroutine(true);// 是否自动开启协程
        $processConfig->setMaxExitWaitTime(3);// 最大退出等待时间
        \EasySwoole\Component\Process\Manager::getInstance()->addProcess(new $class($processConfig));// 注册 IP 限流自定义进程

    }
}